package edu.uiowa.ms.hsm;

import java.io.*;
import java.util.*;

import edu.uiowa.ms.hsm.dijkstra.model.*;

public class UrbanTransit {

	public static final int NOT_ADJACENT = -1;
	/**
	 * @param args
	 */
	public static void main(String[] args) throws IOException {
		int numRoutes = Integer.parseInt(args[0]);
		int maxStopsPerRoute = Integer.parseInt(args[1]);
		int minStopsPerRoute = Integer.parseInt(args[2]);
		int numStops = Integer.parseInt(args[3]);

		//Make demand matrix
		int[][] demand = new int[numStops][numStops];
		BufferedReader reader = new BufferedReader(new FileReader(args[4]));
		
		String line;
		int i = 0;
		while((line = reader.readLine()) != null){
			StringTokenizer st = new StringTokenizer(line);
			int j=0;
			while(st.hasMoreTokens()) {
				String s = st.nextToken();
				demand[i][j] = Integer.parseInt(s);
				j++;
			}
			if(j>1) i++;
		}
		
		//Make time matrix
		//  Time matrix is symmetric, only read in half
		int[][] time = new int[numStops][numStops];
		reader = new BufferedReader(new FileReader(args[5]));
		i = 0;
		while((line = reader.readLine()) != null){
			StringTokenizer st = new StringTokenizer(line);
			int j = 0;
			while(st.hasMoreTokens()) {
				String t = st.nextToken();
				//For nodes not directly connected in our graph, represent value in time matrix as -1
				if (t.equals("-"))
					time[i][j] = UrbanTransit.NOT_ADJACENT;
				else
					time[i][j] = Integer.parseInt(t);
				j++;
			}
			if(j > 1) i++;//skip over blank lines in txt
		}
		
		//Build road network
		List <Vertex> stops = new ArrayList<Vertex>();
		List <Edge> paths = new ArrayList<Edge>();
		for (i = 0; i < time[0].length; i++)
			stops.add(new Vertex(Integer.toString(i), Integer.toString(i)));
		for (i = 0; i < time[0].length; i++)
			//for (int j = i+1; j < time[0].length; j++) {
			for (int j = 0; j < time[0].length;j++){
				if (time[i][j] != UrbanTransit.NOT_ADJACENT)
					paths.add(new Edge(Integer.toString((i*numStops)+j), stops.get(i), stops.get(j), time[i][j]));
			}
		Graph roadNetwork = new Graph(stops, paths);
		
		/*
		//PRINT DEMAND MATRIX:
		for (i = 0; i < demand[0].length; i++) {
			for (int j = 0; j < demand[0].length; j++)
				System.out.print(demand[i][j] + "\t");
			System.out.println();
		}
		
		//PRINT TIME MATRIX:
		for (i = 0; i < time[0].length; i++) {
			for (int j = 0; j < time[0].length; j++)
				System.out.print(time[i][j] + "\t");
			System.out.println();
		}
		
		//PRINT STOPS
		for (i=0; i<stops.size(); i++) {
			System.out.println("Stop: " + stops.get(i).toString());
		}
		
		//PRING EDGES
		for (i=0; i<paths.size(); i++) {
			System.out.println("Edge: " + paths.get(i).toString());
		}
		
		//Tree roadSpanningTree = roadNetwork.spanningTree();
		*/
		
		System.out.println("Starting search...");
		RandomSolution rs = new RandomSolution(numRoutes, maxStopsPerRoute, minStopsPerRoute, demand, time, roadNetwork);
		/*
		rs.printSolution();
		System.out.println("FITNESS = " + rs.fitness());
		rs.makeSmallChange();
		rs.printSolution();
		System.out.println("FITNESS = " + rs.fitness());
		*/
		
		/*for(int k = 0; k < 100; k++){
			rs.printSolution();
			System.out.println("FITNESS = " + rs.fitness());
			rs.makeSmallChange();
		}*/
		
		Solution opt = rs.clone();
		
		long start = System.currentTimeMillis();
		for (i = 0; i<20; i++) {
			System.out.println(i);
			LocalSearch search = new LocalSearch(opt);
			Solution localOpt = search.BestImprovement();
			//Solution localOpt = search.probBestImprovement();
			
			//.printSolution();
			if (localOpt.fitness < opt.fitness){
				System.out.println("*********SWITCH*******");
			    opt = localOpt;
			}else{
				opt.printSolution();
			}
			    
		}
		long end = System.currentTimeMillis();
		
		System.out.println("Optimal FITNESS = " + opt.fitness());
		System.out.println("  - Percent improvement over original random solution "
				+((rs.fitness()-opt.fitness())/(float)rs.fitness())*100F+"%");
		System.out.println("  - Found in "+(end-start)/1000F+" seconds");
		opt.printSolution();
		
		System.out.println("Is our solution feasible? " + opt.isFeasible());
	}
}